#pragma once
#include <iostream>
#include <string>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
#include<fstream>
#include <cstring>
#include<pthread.h>
#define SCREEN_TYPE 1
#define FILE_TYPE 2

enum Level
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

class logmessage
{
public:
    std::string _level;
    pid_t _pid;
    std::string _filename;
    int _fileline;
    std::string _currtime;
   std::string _message_info;
};

std::string LeveltoString(int level)
{
    switch (level)
    {
    case 1:
        return "DEBUG";
        break;
    case 2:
        return "FIFO";
        break;
    case 3:
        return "WARNING";
        break;
    case 4:
        return "ERROR";
        break;
    case 5:
        return "FATAL";
        break;
    default:
        break;
    }
    return nullptr;
}

std::string GetCurrTime()
{
    time_t now = time(nullptr);
    struct tm *Tm = localtime(&now);
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "%d-%02d-%02d-%02d-%02d-%02d", Tm->tm_year + 1900, Tm->tm_mon + 1, Tm->tm_mday, Tm->tm_hour, Tm->tm_min, Tm->tm_sec);
    return buffer;
}

const std::string defaultfile = "./log.txt";
pthread_mutex_t glock=PTHREAD_MUTEX_INITIALIZER;

class Log
{
public:
    Log(const std::string& file = defaultfile)
        : _type(SCREEN_TYPE),_logfile(file)
    {}

    void FlushtoScreen(logmessage *lg)
    {
        printf("[%s][%d][%s][%d][%s]%s",
               lg->_level.c_str(),
               lg->_pid,
               lg->_filename.c_str(),
               lg->_fileline,
               lg->_currtime.c_str(),
               lg->_message_info.c_str());
    }
    void FlushtoFile(logmessage *lg)
    {
        std::ofstream out(_logfile, std::ios::app);
            if (!out.is_open())
                return;
            char logtxt[2048];
            snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s]%s",
               lg->_level.c_str(),
               lg->_pid,
               lg->_filename.c_str(),
               lg->_fileline,
               lg->_currtime.c_str(),
               lg->_message_info.c_str());
            out.write(logtxt, strlen(logtxt));
            out.close();
    }

    void FlushLog(logmessage *lg)
    {
        pthread_mutex_lock(&glock);
        switch (_type)
        {
        case SCREEN_TYPE:
            FlushtoScreen(lg);
            break;
        case FILE_TYPE:
            FlushtoFile(lg);
            break;
        default:
            break;
        }
        pthread_mutex_unlock(&glock);
    }

    void Enable(int type)
    {
        _type = type;
    }

    void LogMessage(int level, std::string filename, int line, std::string format, ...)
    {
        logmessage lg;
        lg._level = LeveltoString(level);
        lg._pid = getpid();
        lg._filename = filename;
        lg._fileline = line;
        lg._currtime = GetCurrTime();
        char buffer[1024];
        va_list ap;
        va_start(ap, format);
        vsnprintf(buffer, sizeof(buffer), format.c_str(), ap);
        va_end(ap);
        lg._message_info = buffer;
        FlushLog(&lg);
    }

private:
    int _type;
    std::string _logfile;
};

Log _log;
#define LOG(level, format, ...)                                           \
    do                                                                    \
    {                                                                     \
        _log.LogMessage(level, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0);
#define Enable_SCREEN()         \
    do                          \
    {                           \
        _log.Enable(SCREEN_TYPE); \
    } while (0);
#define Enable_FILE()         \
    do                        \
    {                         \
        _log.Enable(FILE_TYPE); \
    } while (0);
